package com.mmd.utils;

public class ObfuseTableBase64 {
    private static final int MAX_LENGTH = 5;
    private static final int MAX_CODE_TIME = 3;
    private static final byte placeHolder = 108;
    private static final byte[] encodingTable;
    private static final byte[] decodingTable;

    public ObfuseTableBase64() {
    }

    public static String encode(String data) {
        return new String(encode(data.getBytes()));
    }

    public static byte[] encode(byte[] data) {
        int modulus = data.length % 3;
        byte[] bytes;
        if (modulus == 0) {
            bytes = new byte[4 * data.length / 3];
        } else {
            bytes = new byte[4 * (data.length / 3 + 1)];
        }

        int dataLength = data.length - modulus;
        int b1 = 0;

        int b2;
        for(b2 = 0; b1 < dataLength; b2 += 4) {
            int a1 = data[b1] & 255;
            int a2 = data[b1 + 1] & 255;
            int a3 = data[b1 + 2] & 255;
            bytes[b2] = encodingTable[a1 >>> 2 & 63];
            bytes[b2 + 1] = encodingTable[(a1 << 4 | a2 >>> 4) & 63];
            bytes[b2 + 2] = encodingTable[(a2 << 2 | a3 >>> 6) & 63];
            bytes[b2 + 3] = encodingTable[a3 & 63];
            b1 += 3;
        }

        int d1;
        switch(modulus) {
            case 0:
            default:
                break;
            case 1:
                d1 = data[data.length - 1] & 255;
                b1 = d1 >>> 2 & 63;
                b2 = d1 << 4 & 63;
                bytes[bytes.length - 4] = encodingTable[b1];
                bytes[bytes.length - 3] = encodingTable[b2];
                bytes[bytes.length - 2] = 108;
                bytes[bytes.length - 1] = 108;
                break;
            case 2:
                d1 = data[data.length - 2] & 255;
                int d2 = data[data.length - 1] & 255;
                b1 = d1 >>> 2 & 63;
                b2 = (d1 << 4 | d2 >>> 4) & 63;
                int b3 = d2 << 2 & 63;
                bytes[bytes.length - 4] = encodingTable[b1];
                bytes[bytes.length - 3] = encodingTable[b2];
                bytes[bytes.length - 2] = encodingTable[b3];
                bytes[bytes.length - 1] = 108;
        }

        return bytes;
    }

    public static String doDecode(String data) {
        return new String(decode(data.getBytes()));
    }

    public static byte[] decode(byte[] data) {
        data = discardNonBase64Bytes(data);
        byte[] bytes;
        if (data[data.length - 2] == 108) {
            bytes = new byte[(data.length / 4 - 1) * 3 + 1];
        } else if (data[data.length - 1] == 108) {
            bytes = new byte[(data.length / 4 - 1) * 3 + 2];
        } else {
            bytes = new byte[data.length / 4 * 3];
        }

        int i = 0;

        byte b1;
        byte b2;
        byte b3;
        byte b4;
        for(int j = 0; i < data.length - 4; j += 3) {
            b1 = decodingTable[data[i]];
            b2 = decodingTable[data[i + 1]];
            b3 = decodingTable[data[i + 2]];
            b4 = decodingTable[data[i + 3]];
            bytes[j] = (byte)(b1 << 2 | b2 >> 4);
            bytes[j + 1] = (byte)(b2 << 4 | b3 >> 2);
            bytes[j + 2] = (byte)(b3 << 6 | b4);
            i += 4;
        }

        if (data[data.length - 2] == 108) {
            b1 = decodingTable[data[data.length - 4]];
            b2 = decodingTable[data[data.length - 3]];
            bytes[bytes.length - 1] = (byte)(b1 << 2 | b2 >> 4);
        } else if (data[data.length - 1] == 108) {
            b1 = decodingTable[data[data.length - 4]];
            b2 = decodingTable[data[data.length - 3]];
            b3 = decodingTable[data[data.length - 2]];
            bytes[bytes.length - 2] = (byte)(b1 << 2 | b2 >> 4);
            bytes[bytes.length - 1] = (byte)(b2 << 4 | b3 >> 2);
        } else {
            b1 = decodingTable[data[data.length - 4]];
            b2 = decodingTable[data[data.length - 3]];
            b3 = decodingTable[data[data.length - 2]];
            b4 = decodingTable[data[data.length - 1]];
            bytes[bytes.length - 3] = (byte)(b1 << 2 | b2 >> 4);
            bytes[bytes.length - 2] = (byte)(b2 << 4 | b3 >> 2);
            bytes[bytes.length - 1] = (byte)(b3 << 6 | b4);
        }

        return bytes;
    }

    public static byte[] decode(String data) {
        data = discardNonBase64Chars(data);
        byte[] bytes;
        if (data.charAt(data.length() - 2) == 'l') {
            bytes = new byte[(data.length() / 4 - 1) * 3 + 1];
        } else if (data.charAt(data.length() - 1) == 'l') {
            bytes = new byte[(data.length() / 4 - 1) * 3 + 2];
        } else {
            bytes = new byte[data.length() / 4 * 3];
        }

        int i = 0;

        byte b1;
        byte b2;
        byte b3;
        byte b4;
        for(int j = 0; i < data.length() - 4; j += 3) {
            b1 = decodingTable[data.charAt(i)];
            b2 = decodingTable[data.charAt(i + 1)];
            b3 = decodingTable[data.charAt(i + 2)];
            b4 = decodingTable[data.charAt(i + 3)];
            bytes[j] = (byte)(b1 << 2 | b2 >> 4);
            bytes[j + 1] = (byte)(b2 << 4 | b3 >> 2);
            bytes[j + 2] = (byte)(b3 << 6 | b4);
            i += 4;
        }

        if (data.charAt(data.length() - 2) == 'l') {
            b1 = decodingTable[data.charAt(data.length() - 4)];
            b2 = decodingTable[data.charAt(data.length() - 3)];
            bytes[bytes.length - 1] = (byte)(b1 << 2 | b2 >> 4);
        } else if (data.charAt(data.length() - 1) == 'l') {
            b1 = decodingTable[data.charAt(data.length() - 4)];
            b2 = decodingTable[data.charAt(data.length() - 3)];
            b3 = decodingTable[data.charAt(data.length() - 2)];
            bytes[bytes.length - 2] = (byte)(b1 << 2 | b2 >> 4);
            bytes[bytes.length - 1] = (byte)(b2 << 4 | b3 >> 2);
        } else {
            b1 = decodingTable[data.charAt(data.length() - 4)];
            b2 = decodingTable[data.charAt(data.length() - 3)];
            b3 = decodingTable[data.charAt(data.length() - 2)];
            b4 = decodingTable[data.charAt(data.length() - 1)];
            bytes[bytes.length - 3] = (byte)(b1 << 2 | b2 >> 4);
            bytes[bytes.length - 2] = (byte)(b2 << 4 | b3 >> 2);
            bytes[bytes.length - 1] = (byte)(b3 << 6 | b4);
        }

        return bytes;
    }

    private static byte[] discardNonBase64Bytes(byte[] data) {
        byte[] temp = new byte[data.length];
        int bytesCopied = 0;

        for(int i = 0; i < data.length; ++i) {
            if (isValidBase64Byte(data[i])) {
                temp[bytesCopied++] = data[i];
            }
        }

        byte[] newData = new byte[bytesCopied];
        System.arraycopy(temp, 0, newData, 0, bytesCopied);
        return newData;
    }

    private static String discardNonBase64Chars(String data) {
        StringBuffer sb = new StringBuffer();
        int length = data.length();

        for(int i = 0; i < length; ++i) {
            if (isValidBase64Byte((byte)data.charAt(i))) {
                sb.append(data.charAt(i));
            }
        }

        return sb.toString();
    }

    private static boolean isValidBase64Byte(byte b) {
        if (b == 108) {
            return true;
        } else if (b >= 0 && b < 128) {
            return decodingTable[b] != -1;
        } else {
            return false;
        }
    }

    public static String doDecode(byte[] data, int decodeTime) {
        if (decodeTime > 5) {
            decodeTime = 3;
        }

        byte[] decode = data;

        for(int i = 0; i < decodeTime; ++i) {
            decode = decode(decode);
        }

        String name = new String(decode);
        return name;
    }

    public static void main(String[] args) {
        System.out.println(doDecode("fuKWChCKpQRl"));
    }

    static {
        byte[][] data2Arr = new byte[5][13];
        byte[] tmp0 = new byte[]{48, 57, 95, 45, 49, 56, 54, 51, 52, 50, 55, 53, 35};
        byte[] tmp1 = new byte[tmp0.length];

        for(int i = 0; i < tmp1.length; ++i) {
            tmp1[i] = (byte)(i + 65);
        }

        byte[] tmp2 = new byte[tmp1.length];

        for(int i = 0; i < tmp2.length; ++i) {
            tmp2[i] = (byte)(tmp1[tmp1.length - 1] + i + 1);
        }

        byte[] tmp3 = new byte[tmp2.length];

        for(int i = 0; i < tmp3.length; ++i) {
            tmp3[i] = (byte)(i + 97);
        }

        byte[] tmp4 = new byte[tmp3.length];

        for(int i = 0; i < tmp3.length; ++i) {
            tmp4[i] = (byte)(tmp3[tmp3.length - 1] + i + 1);
        }

        data2Arr[0] = tmp3;
        data2Arr[1] = tmp0;
        data2Arr[2] = tmp1;
        data2Arr[3] = tmp4;
        data2Arr[4] = tmp2;
        byte[] data65 = new byte[data2Arr[0].length * data2Arr.length];
        int idx = 0;

        for(int i = 0; i < data65.length; ++i) {
            int quotient = i / 5;
            int remainder = i % 5;
            data65[i] = data2Arr[remainder][quotient];
            if (data65[i] == 108) {
                idx = i;
            }
        }

        byte[] data64 = new byte[data65.length - 1];
        System.arraycopy(data65, 0, data64, 0, idx);
        System.arraycopy(data65, idx + 1, data64, idx, 64 - idx);
        encodingTable = data64;
        decodingTable = new byte[data65.length - 1 << 1];

        for(int i = 0; i < encodingTable.length; ++i) {
            decodingTable[encodingTable[i]] = (byte)i;
        }

    }
}
